/******************************************************************************
 *                                                                            *
 *               (c) 2008-2009 HutLab. All rights reserved.                   *
 *                        Eduardo Henrique Leite                              *
 *                                                                            *
 ******************************************************************************

 History:
 Created 02/01/2009

 */

package hutlab.sudoku.engine;

import javax.microedition.lcdui.*;
import hutlab.sudoku.engine.base.*;
import hutlab.sudoku.engine.ui.*;
import hutlab.sudoku.*;

/**
 * Sudoku Game Canvas class.
 */
public class SudokuGameCanvas extends Canvas {

    // ----- FIELDS -----

    private SudokuEngine mEngine;
    private Menu mMenu;
    private SudokuBoard mBoard;
    private Display mDisplay;
    private SudokuGameRun mMidlet;
    private SudokuTime mTimer;

    // ----- GETTERS -----

    /**
     * Returns the Engine object.
     */
    public SudokuEngine getEngine(){
        if (mEngine == null){
            mEngine = new SudokuEngine();
        }

        return mEngine;
    }

    /**
     * Returns the Display object.
     */
    public Display getDisplay(){
        return mDisplay;
    }

    // ----- SETTERS -----

    /**
     * Sets the display object.
     */
    public void setDisplay(Display display){
        mDisplay = display;
    }

    // ----- CONSTRUCTOR -----

    public SudokuGameCanvas(SudokuGameRun midlet){
        this.mMidlet = midlet;

        this.setFullScreenMode(true);
    }

    // ----- PUBLIC -----

    /**
     * Canvas.paint method override.
     */
    public void paint(Graphics graphics){
        SudokuEngine engine = this.getEngine();

        clear(graphics, ColourRGB.White());
        
        switch (engine.getState()){
            case SudokuGameState.SUDOKU_MAINMENU :
                renderMenu(graphics);
                break;
            case SudokuGameState.SUDOKU_CREATEPUZZLE :
                renderBoard(graphics);
                renderCreatePuzzleMenu(graphics);
                break;
            case SudokuGameState.SUDOKU_PLAYCREATED:
                renderBoard(graphics);
                renderClock(graphics);
                renderPlayMenu(graphics);
                break;
        }
    }

    /**
     * Canvas.keyPressed method override.
     */
    public void keyPressed(int keyCode){
        SudokuEngine engine = this.getEngine();
        boolean doRepaint = false;

        switch (engine.getState()){
            case SudokuGameState.SUDOKU_MAINMENU :
                runCommand(this.mMenu.keyCommand(keyCode));
                doRepaint = true;
                break;
            case SudokuGameState.SUDOKU_CREATEPUZZLE :
                this.mBoard.keyCommand(keyCode);
                handleCreatePuzzleMenu(keyCode);
                doRepaint = true;
                break;
            case SudokuGameState.SUDOKU_PLAYCREATED:
                this.mBoard.keyCommand(keyCode);
                handlePlayMenu(keyCode);
                doRepaint = true;
                break;
        }

        if (doRepaint){
            repaint();
        }
    }

    // ----- PRIVATE -----

    /**
     * Renders the bottom menu on CreatePuzzle page.
     */
    private void renderCreatePuzzleMenu(Graphics graphics){
        graphics.setGrayScale(0);
        graphics.setFont(Font.getDefaultFont());
        graphics.drawString("BACK", 3, this.getHeight(), Graphics.LEFT | Graphics.BOTTOM);
        graphics.drawString("PLAY", this.getWidth() - 3, this.getHeight(), Graphics.RIGHT | Graphics.BOTTOM);
    }

    /**
     * Renders the bottom menu on Play page.
     */
    private void renderPlayMenu(Graphics graphics){
        graphics.setGrayScale(0);
        graphics.setFont(Font.getDefaultFont());
        graphics.drawString("BACK", 3, this.getHeight(), Graphics.LEFT | Graphics.BOTTOM);
        graphics.drawString("SOLUTION", this.getWidth() - 3, this.getHeight(), Graphics.RIGHT | Graphics.BOTTOM);
    }

    /**
     * Renders the timer clock on Play page.
     */
    private void renderClock(Graphics graphics){
        if (this.mTimer == null){
            this.mTimer = new SudokuTime(this);
            this.mTimer.setFont(Font.getDefaultFont());
            this.mTimer.setFontColour(ColourRGB.Black());
            this.mTimer.setX(getWidth() - 10);
            this.mTimer.setY(40);
            this.mTimer.setAutoRefresh(true);
            this.mTimer.Start();
        }

        this.mTimer.render(graphics);
    }

    /**
     * Keyboard handler for CreatePuzzle menu.
     */
    private void handleCreatePuzzleMenu(int keyCode){
        switch (keyCode){
            case GameObject.SUDOKU_COMMAND_LEFT:
                runBackToMainMenu();
                break;
            case GameObject.SUDOKU_COMMAND_RIGHT:
                runPlayCreatedGame();
                break;
        }
    }

    /**
     * Keyboard handler for Play menu.
     */
    private void handlePlayMenu(int keyCode){
        switch(keyCode){
            case GameObject.SUDOKU_COMMAND_LEFT:
                runBackToMainMenu();
                break;
            case GameObject.SUDOKU_COMMAND_RIGHT:
                runShowSolution();
                break;
        }
    }

    /**
     * Solution menu command.
     */
    private void runShowSolution(){
        //UiHelper.alert(this.mDisplay, "SUDOKU",
        //        "Not implemented yet", AlertType.INFO);

        if (this.mEngine.getSolution(this.mBoard)){
            this.mTimer.Stop();
            this.mBoard.setMode(SudokuBoardMode.SHOW);
        }
        else{
            UiHelper.alert(this.mDisplay, "SUDOKU",
                    "Cannot find a solution.", AlertType.ERROR);
        }
    }

    /**
     * Back to main menu command.
     */
    private void runBackToMainMenu(){
        this.mBoard.clear();
        mEngine.setState(SudokuGameState.SUDOKU_MAINMENU);
    }

    /**
     * Play created game command.
     */
    private void runPlayCreatedGame(){
        if (this.mBoard.getGeneratedNumber() < 4){
            UiHelper.alert(this.mDisplay, "Sudoku",
                    "Please enter at least 4 numbers before playing the puzzle.",
                    AlertType.ERROR);
        }
        else{
            if (this.mBoard.checkConflicts()){
            UiHelper.alert(this.mDisplay, "Sudoku",
                    "Please check the puzzle. It's invalid.",
                    AlertType.ERROR);
            }
            else{
                this.mEngine.setState(SudokuGameState.SUDOKU_PLAYCREATED);
                this.mBoard.setSelectedCellIndex(0);
                this.mBoard.setMode(SudokuBoardMode.PLAY);
            }
        }
    }

    /**
     * If any command is returned from menu, run it.
     */
    private void runCommand(int commandIndex){
        if (commandIndex >= 0){
            switch (commandIndex){
                case 0:
                    runPlay();
                    break;
                case 1:
                    runCreatePuzzle();
                    break;
                case 2:
                    runExit();
                    break;
            }
        }
    }

    /**
     * Run play command.
     */
    private void runPlay(){        
        UiHelper.alert(this.mDisplay, "Sudoku", "This has not been implemented yet.",
                AlertType.WARNING);
    }

    /**
     * Create puzzle menu command.
     */
    private void runCreatePuzzle(){
        this.mEngine.setState(SudokuGameState.SUDOKU_CREATEPUZZLE);
        if (this.mBoard == null){
            createBoard();
        }
        this.mBoard.setSelectedCellIndex(0);
        this.mBoard.setMode(SudokuBoardMode.BUILD);
    }

    /**
     * Run exit command.
     */
    private void runExit(){
        this.mMidlet.exit();
    }

    /**
     * Clears the screen with the given colour.
     */
    private void clear(Graphics graphics, ColourRGB colour){
        graphics.setColor(
                colour.getRed(),
                colour.getGreen(),
                colour.getBlue());
        graphics.fillRect(0, 0, getWidth(), getHeight());
    }

    /**
     * Renders the menu.
     */
    private void renderMenu(Graphics graphics){
        if (this.mMenu == null){
            createMenu();
        }

        this.mMenu.render(graphics);
    }

    /**
     * Renders the board.
     */
    private void renderBoard(Graphics graphics){
        if (this.mBoard == null){
            createBoard();
        }

        this.mBoard.render(graphics);
    }

    /**
     * Creates the menu object.
     */
    private void createMenu(){
        ArrayList items = new ArrayList();
        items.add(new MenuItem("Play"));
        items.add(new MenuItem("Create Puzzle"));
        items.add(new MenuItem("Exit"));

        this.mMenu = new Menu();
        this.mMenu.setActiveColour(ColourRGB.Red());
        this.mMenu.setColour(ColourRGB.Black());
        this.mMenu.setFont(Font.getDefaultFont());
        this.mMenu.setX(getWidth() / 2);
        this.mMenu.setY(0);
        this.mMenu.setCentred(true);
        this.mMenu.setItems(items);
        this.mMenu.setActiveIndex(0);
    }

    /**
     * Creates the board object.
     */
    private void createBoard(){
        this.mBoard = new SudokuBoard(this.mDisplay, SudokuBoardMode.BUILD);
        this.mBoard.setX(10);
        this.mBoard.setY(10);
        this.mBoard.setBorderColour(ColourRGB.Black());
        this.mBoard.setCellColour(ColourRGB.White());
        this.mBoard.setCellSize(20);
        this.mBoard.setGeneratedCellColour(ColourRGB.Grey());
        this.mBoard.setConflictedCellColour(ColourRGB.Yellow());
        this.mBoard.setSelectedCellColour(ColourRGB.Red());
        this.mBoard.setSuggestedCellColour(ColourRGB.Blue());
        this.mBoard.setFontColour(ColourRGB.Black());
        this.mBoard.setFont(Font.getDefaultFont());
        
        this.mBoard.setSelectedCellIndex(0);
    }
}
